# -*- mode: python -*-

__revision__ = '$Hg: config.py,v  2006/09/02 17:40:06 +0200 $'

# ddb imports
from ddb.settings import *

# twisted imports
from twisted.internet import reactor

# system imports
import os, pickle, sys, time, types

try:
    import yaml
except:
    yaml = None

class configFile(dict):
    file    = ''
    formats = ['ini', 'pickle', 'yaml']
    format  = formats[0]
    changed = False

    def __init__(self, file, format=None, mandatory=False):
        self.file      = file
        if format:
            self.format = format
        else:
            self.format = file.split('.')[-1]
        self.mandatory = mandatory

    def autosave(self):
        """Autosave loop, if changed."""
        if self.changed:
            self.save(True)
        reactor.callLater(CONFIG_AUTOSAVE, self.autosave)

    def load(self, autosave=True):
        try:
            fp = open(self.file, 'r')
        except Exception, e:
            print 'unable to open configuration file %s: %s' % (self.file,
                str(e))
            if self.mandatory:
                sys.exit(1)
            return
        if self.format == 'ini':
            data = fp.readlines()
            self.parseIni(data)
            del data
            fp.close()
        elif self.format == 'pickle':
            data = pickle.load(fp)
            for section in data.keys():
                self[section] = data[section].copy()
            del data
            fp.close()
        elif self.format == 'yaml':
            data = yaml.load(fp)
            for section in data.keys():
                self[section] = data[section].copy()
            del data
            fp.close()
        else:
            raise Exception('Can not handle unknown configuration type "%s"' % self.format)
            return
        if autosave:
            self.autosave()
        
    def parseIni(self, data):
        lineNumber = 0
        section    = 'general'
        for line in data:
            line = line.strip()
            lineNumber += 1
            if not line or line[0] == '#':
                pass
            elif line[0] == '[' and line[-1] == ']':
                section = line[1:-1]
                self.sectionAdd(section)
            elif '=' in line:
                item, value = line.split('=', 1)
                self.sectionAddItem(section, item.strip(), value.strip())
            else:
                print 'parse error in %s line %d' % (self.file, lineNumber)

    def save(self, autosave=False):
        """Save data in the given format."""
        if os.path.isfile(self.file):
            os.rename(self.file, '%s%s' % (self.file, CONFIG_BACKUP_SUFFIX))
        fp = open(self.file, 'w')
        if self.format == 'ini':
            fp.write('#\n# Configuration saved at %s\n#\n\n' %
                time.strftime('%D %T'))
            sections = self.keys()
            sections.sort()
            for section in sections:
                fp.write('[%s]\n' % section)
                items = self[section].keys()
                items.sort()
                for item in items:
                    for value in self[section][item]:
                        fp.write('%s=%s\n' % (item, value))
                fp.write('\n')
            fp.write('# EOF\n')
            fp.close()
        elif self.format == 'pickle':
            pickle.dump(self.copy(), fp)
            fp.close()
        elif self.format == 'yaml':
            yaml.dump(self.copy(), fp)
            fp.close()
        self.changed = False

    def get(self, section, option):
        if not self.optionExists(section, option):
            return None
        return self[section][option]

    def set(self, section, option, value):
        if not self.sectionExists(section):
            self.sectionAdd(section)
        if type(value) != types.ListType:
            value = [value]
        self[section][option] = value
        self.changed = True

    def sections(self):
        return self.keys()

    def sectionAdd(self, section):
        self[section] = {}
        self.changed = True

    def sectionAddItem(self, section, item, value):
        if not self.sectionExists(section):
            self.sectionAdd(section)
        if self[section].has_key(item):
            self[section][item].append(value)
        else:
            self[section][item] = [value]

    def sectionExists(self, section):
        return self.has_key(section)

    def sectionGet(self, section):
        if not self.sectionExists(section):
            return None
        else:
            return self[section]

    def sectionRemove(self, section):
        if self.sectionExists(section):
            del self[section]

    def optionExists(self, section, option):
        return self.sectionExists(section) and self[section].has_key(option)

